package com.bi.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bi.common.PageResult;
import com.bi.common.ResultCode;
import com.bi.dto.CreateWideTableDTO;
import com.bi.dto.WideTableDataQueryDTO;
import com.bi.dto.WideTableQueryDTO;
import com.bi.entity.WideTable;
import com.bi.entity.WideTableField;
import com.bi.exception.BusinessException;
import com.bi.mapper.WideTableFieldMapper;
import com.bi.mapper.WideTableMapper;
import com.bi.service.ElasticsearchService;
import com.bi.service.WideTableService;
import com.bi.vo.WideTableDataVO;
import com.bi.vo.WideTableFieldVO;
import com.bi.vo.WideTableVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 数据宽表服务实现类
 * 
 * @author BI Platform Team
 * @since 1.0.0
 */
@Slf4j
@Service
public class WideTableServiceImpl implements WideTableService {

    @Autowired
    private WideTableMapper wideTableMapper;

    @Autowired
    private WideTableFieldMapper wideTableFieldMapper;

    @Autowired
    private ElasticsearchService elasticsearchService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createWideTable(CreateWideTableDTO createDTO, String creator) {
        log.info("创建数据宽表，名称: {}, 创建人: {}", createDTO.getName(), creator);

        // 检查宽表名称是否已存在
        if (!isWideTableNameAvailable(createDTO.getName(), null)) {
            throw new BusinessException(ResultCode.CONFLICT, "宽表名称已存在");
        }

        // 创建宽表基本信息
        WideTable wideTable = new WideTable();
        wideTable.setName(createDTO.getName());
        wideTable.setDisplayName(createDTO.getDisplayName());
        wideTable.setDescription(createDTO.getDescription());
        wideTable.setType(createDTO.getType());
        wideTable.setDataSource(createDTO.getDataSource());
        wideTable.setTableConfig(createDTO.getTableConfig());
        wideTable.setStatus(WideTable.WideTableStatus.INACTIVE);
        wideTable.setCreator(creator);

        // 生成Elasticsearch索引名称
        if (!StringUtils.hasText(createDTO.getEsIndexName())) {
            String esIndexName = generateEsIndexName(createDTO.getName());
            wideTable.setEsIndexName(esIndexName);
        } else {
            wideTable.setEsIndexName(createDTO.getEsIndexName());
        }

        // 保存宽表信息
        wideTableMapper.insert(wideTable);
        Long wideTableId = wideTable.getId();

        // 保存字段信息
        saveWideTableFields(wideTableId, createDTO.getFields());

        // 如果是在线宽表，创建Elasticsearch索引
        if (WideTable.WideTableType.ONLINE.equals(createDTO.getType())) {
            boolean indexCreated = initOnlineWideTableIndex(wideTableId);
            if (!indexCreated) {
                throw new BusinessException(ResultCode.INTERNAL_SERVER_ERROR, "创建Elasticsearch索引失败");
            }
        }

        log.info("数据宽表创建成功，ID: {}", wideTableId);
        return wideTableId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateWideTable(Long id, CreateWideTableDTO createDTO, String operator) {
        log.info("更新数据宽表，ID: {}, 操作人: {}", id, operator);

        WideTable existingTable = wideTableMapper.selectById(id);
        if (existingTable == null) {
            throw new BusinessException(ResultCode.NOT_FOUND, "数据宽表不存在");
        }

        // 检查宽表名称是否已存在（排除自己）
        if (!isWideTableNameAvailable(createDTO.getName(), id)) {
            throw new BusinessException(ResultCode.CONFLICT, "宽表名称已存在");
        }

        // 更新基本信息
        existingTable.setName(createDTO.getName());
        existingTable.setDisplayName(createDTO.getDisplayName());
        existingTable.setDescription(createDTO.getDescription());
        existingTable.setDataSource(createDTO.getDataSource());
        existingTable.setTableConfig(createDTO.getTableConfig());
        existingTable.setUpdateTime(LocalDateTime.now());

        wideTableMapper.updateById(existingTable);

        // 更新字段信息
        updateWideTableFields(id, createDTO.getFields(), operator);

        // 同步到Elasticsearch
        if (WideTable.WideTableType.ONLINE.equals(existingTable.getType())) {
            syncWideTableToEs(id);
        }

        log.info("数据宽表更新成功，ID: {}", id);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteWideTable(Long id, String operator) {
        log.info("删除数据宽表，ID: {}, 操作人: {}", id, operator);

        WideTable wideTable = wideTableMapper.selectById(id);
        if (wideTable == null) {
            throw new BusinessException(ResultCode.NOT_FOUND, "数据宽表不存在");
        }

        // 删除Elasticsearch索引
        if (WideTable.WideTableType.ONLINE.equals(wideTable.getType()) 
            && StringUtils.hasText(wideTable.getEsIndexName())) {
            elasticsearchService.deleteIndex(wideTable.getEsIndexName());
        }

        // 删除宽表字段
        wideTableFieldMapper.deleteByWideTableId(id);

        // 删除宽表（逻辑删除）
        wideTableMapper.deleteById(id);

        log.info("数据宽表删除成功，ID: {}", id);
        return true;
    }

    @Override
    public WideTableVO getWideTableById(Long id) {
        WideTableVO wideTableVO = wideTableMapper.selectWideTableById(id);
        if (wideTableVO == null) {
            throw new BusinessException(ResultCode.NOT_FOUND, "数据宽表不存在");
        }

        // 设置类型和状态描述
        enrichWideTableVO(wideTableVO);

        // 查询字段信息
        List<WideTableFieldVO> fields = wideTableFieldMapper.selectByWideTableId(id);
        wideTableVO.setFields(fields);

        // 统计字段数量
        wideTableVO.setDisplayFieldCount((int) fields.stream().mapToLong(f -> f.getIsDisplayField() ? 1 : 0).sum());
        wideTableVO.setQueryFieldCount((int) fields.stream().mapToLong(f -> f.getIsQueryField() ? 1 : 0).sum());

        // 获取数据总数
        if (WideTable.WideTableType.ONLINE.name().equals(wideTableVO.getType().name()) 
            && StringUtils.hasText(wideTableVO.getEsIndexName())) {
            long dataCount = elasticsearchService.getDocumentCount(wideTableVO.getEsIndexName());
            wideTableVO.setDataCount(dataCount);
        }

        return wideTableVO;
    }

    @Override
    public PageResult<WideTableVO> getWideTablePage(WideTableQueryDTO queryDTO) {
        Page<WideTableVO> page = new Page<>(queryDTO.getCurrent(), queryDTO.getSize());
        IPage<WideTableVO> pageResult = wideTableMapper.selectWideTablePage(page, queryDTO);

        List<WideTableVO> records = pageResult.getRecords();
        
        // enrichment each record
        records.forEach(this::enrichWideTableVO);

        return PageResult.of(
            queryDTO.getCurrent(),
            queryDTO.getSize(),
            pageResult.getTotal(),
            records
        );
    }

    @Override
    public List<WideTableVO> getAccessibleWideTableList(String creator) {
        List<WideTableVO> wideTableList = wideTableMapper.selectAccessibleWideTableList(creator);
        wideTableList.forEach(this::enrichWideTableVO);
        return wideTableList;
    }

    @Override
    public List<WideTableFieldVO> getWideTableFields(Long wideTableId) {
        List<WideTableFieldVO> fields = wideTableFieldMapper.selectByWideTableId(wideTableId);
        
        // 设置字段类型描述
        fields.forEach(field -> {
            if (field.getFieldType() != null) {
                field.setFieldTypeDescription(field.getFieldType().getDescription());
            }
        });
        
        return fields;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateWideTableFields(Long wideTableId, List<CreateWideTableDTO.WideTableFieldDTO> fields, String operator) {
        log.info("更新宽表字段配置，宽表ID: {}, 操作人: {}", wideTableId, operator);

        // 删除原有字段
        wideTableFieldMapper.deleteByWideTableId(wideTableId);

        // 保存新字段
        saveWideTableFields(wideTableId, fields);

        log.info("宽表字段配置更新成功，宽表ID: {}", wideTableId);
        return true;
    }

    @Override
    public boolean initOnlineWideTableIndex(Long wideTableId) {
        log.info("初始化在线数据宽表索引，宽表ID: {}", wideTableId);

        WideTable wideTable = wideTableMapper.selectById(wideTableId);
        if (wideTable == null) {
            throw new BusinessException(ResultCode.NOT_FOUND, "数据宽表不存在");
        }

        if (!WideTable.WideTableType.ONLINE.equals(wideTable.getType())) {
            throw new BusinessException(ResultCode.BAD_REQUEST, "只有在线宽表才能初始化索引");
        }

        String indexName = wideTable.getEsIndexName();
        
        // 检查索引是否已存在
        if (elasticsearchService.indexExists(indexName)) {
            log.warn("Elasticsearch索引已存在: {}", indexName);
            return true;
        }

        // 构建索引映射
        Map<String, Object> mapping = buildEsMapping(wideTableId);
        
        // 创建索引
        boolean created = elasticsearchService.createIndex(indexName, mapping);
        
        if (created) {
            log.info("在线数据宽表索引初始化成功，索引名称: {}", indexName);
        } else {
            log.error("在线数据宽表索引初始化失败，索引名称: {}", indexName);
        }
        
        return created;
    }

    @Override
    public WideTableDataVO queryWideTableData(WideTableDataQueryDTO queryDTO) {
        log.info("查询宽表数据，宽表ID: {}", queryDTO.getWideTableId());

        WideTable wideTable = wideTableMapper.selectById(queryDTO.getWideTableId());
        if (wideTable == null) {
            throw new BusinessException(ResultCode.NOT_FOUND, "数据宽表不存在");
        }

        if (!WideTable.WideTableStatus.ACTIVE.equals(wideTable.getStatus())) {
            throw new BusinessException(ResultCode.BAD_REQUEST, "数据宽表未激活");
        }

        String indexName = wideTable.getEsIndexName();
        if (!StringUtils.hasText(indexName)) {
            throw new BusinessException(ResultCode.BAD_REQUEST, "未配置Elasticsearch索引");
        }

        // 查询数据
        WideTableDataVO result = elasticsearchService.queryWideTableData(indexName, queryDTO);
        
        // 设置字段信息
        List<WideTableFieldVO> fields = getWideTableFields(queryDTO.getWideTableId());
        List<WideTableDataVO.FieldInfo> fieldInfos = convertToFieldInfos(fields);
        result.setFieldInfos(fieldInfos);

        return result;
    }

    @Override
    public Map<String, Object> aggregateWideTableData(WideTableDataQueryDTO queryDTO) {
        log.info("聚合查询宽表数据，宽表ID: {}", queryDTO.getWideTableId());

        WideTable wideTable = wideTableMapper.selectById(queryDTO.getWideTableId());
        if (wideTable == null) {
            throw new BusinessException(ResultCode.NOT_FOUND, "数据宽表不存在");
        }

        String indexName = wideTable.getEsIndexName();
        return elasticsearchService.aggregateQuery(indexName, queryDTO);
    }

    @Override
    public String exportWideTableData(WideTableDataQueryDTO queryDTO) {
        // 实现数据导出逻辑，这里简化返回文件路径
        log.info("导出宽表数据，宽表ID: {}", queryDTO.getWideTableId());
        
        // 实际实现中会生成Excel文件并返回文件路径
        return "/exports/wide_table_" + queryDTO.getWideTableId() + "_" + System.currentTimeMillis() + ".xlsx";
    }

    @Override
    public WideTableDataVO.FieldStats getFieldStats(Long wideTableId, String fieldName) {
        WideTable wideTable = wideTableMapper.selectById(wideTableId);
        if (wideTable == null) {
            throw new BusinessException(ResultCode.NOT_FOUND, "数据宽表不存在");
        }

        String indexName = wideTable.getEsIndexName();
        return elasticsearchService.getFieldStats(indexName, fieldName);
    }

    @Override
    public boolean isWideTableNameAvailable(String name, Long excludeId) {
        LambdaQueryWrapper<WideTable> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WideTable::getName, name);
        if (excludeId != null) {
            queryWrapper.ne(WideTable::getId, excludeId);
        }
        
        return wideTableMapper.selectCount(queryWrapper) == 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateWideTableStatus(Long id, WideTable.WideTableStatus status, String operator) {
        log.info("更新宽表状态，ID: {}, 状态: {}, 操作人: {}", id, status, operator);

        int updated = wideTableMapper.updateStatus(id, status);
        if (updated > 0) {
            log.info("宽表状态更新成功，ID: {}", id);
            return true;
        }
        
        return false;
    }

    @Override
    public boolean syncWideTableToEs(Long wideTableId) {
        log.info("同步宽表结构到Elasticsearch，宽表ID: {}", wideTableId);

        WideTable wideTable = wideTableMapper.selectById(wideTableId);
        if (wideTable == null || !WideTable.WideTableType.ONLINE.equals(wideTable.getType())) {
            return false;
        }

        String indexName = wideTable.getEsIndexName();
        Map<String, Object> mapping = buildEsMapping(wideTableId);
        
        return elasticsearchService.updateMapping(indexName, mapping);
    }

    @Override
    public long getWideTableDataCount(Long wideTableId) {
        WideTable wideTable = wideTableMapper.selectById(wideTableId);
        if (wideTable == null || !StringUtils.hasText(wideTable.getEsIndexName())) {
            return 0L;
        }

        return elasticsearchService.getDocumentCount(wideTable.getEsIndexName());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean clearWideTableData(Long wideTableId, String operator) {
        log.info("清空宽表数据，宽表ID: {}, 操作人: {}", wideTableId, operator);

        WideTable wideTable = wideTableMapper.selectById(wideTableId);
        if (wideTable == null) {
            throw new BusinessException(ResultCode.NOT_FOUND, "数据宽表不存在");
        }

        String indexName = wideTable.getEsIndexName();
        if (StringUtils.hasText(indexName)) {
            // 删除并重新创建索引
            elasticsearchService.deleteIndex(indexName);
            Map<String, Object> mapping = buildEsMapping(wideTableId);
            boolean created = elasticsearchService.createIndex(indexName, mapping);
            
            log.info("宽表数据清空{}，宽表ID: {}", created ? "成功" : "失败", wideTableId);
            return created;
        }
        
        return true;
    }

    /**
     * 保存宽表字段
     */
    private void saveWideTableFields(Long wideTableId, List<CreateWideTableDTO.WideTableFieldDTO> fieldDTOs) {
        if (CollectionUtils.isEmpty(fieldDTOs)) {
            return;
        }

        List<WideTableField> fields = fieldDTOs.stream()
            .map(dto -> {
                WideTableField field = new WideTableField();
                field.setWideTableId(wideTableId);
                field.setFieldName(dto.getFieldName());
                field.setDisplayName(dto.getDisplayName());
                field.setFieldType(dto.getFieldType());
                field.setFieldLength(dto.getFieldLength());
                field.setPrecisionScale(dto.getPrecisionScale());
                field.setNullable(dto.getNullable());
                field.setDefaultValue(dto.getDefaultValue());
                field.setDescription(dto.getDescription());
                field.setIsDisplayField(dto.getIsDisplayField());
                field.setIsQueryField(dto.getIsQueryField());
                field.setSortOrder(dto.getSortOrder());
                return field;
            })
            .collect(Collectors.toList());

        wideTableFieldMapper.batchInsert(fields);
    }

    /**
     * 生成Elasticsearch索引名称
     */
    private String generateEsIndexName(String wideTableName) {
        // 将宽表名称转换为符合Elasticsearch索引命名规范的格式
        String indexName = "wide_table_" + wideTableName.toLowerCase()
            .replaceAll("[^a-z0-9_]", "_")
            .replaceAll("_{2,}", "_");
        
        // 确保索引名称不以下划线开头或结尾
        indexName = indexName.replaceAll("^_+|_+$", "");
        
        return indexName;
    }

    /**
     * 构建Elasticsearch映射
     */
    private Map<String, Object> buildEsMapping(Long wideTableId) {
        List<WideTableFieldVO> fields = getWideTableFields(wideTableId);
        
        Map<String, Object> mapping = new HashMap<>();
        Map<String, Object> properties = new HashMap<>();
        
        for (WideTableFieldVO field : fields) {
            Map<String, Object> fieldMapping = new HashMap<>();
            
            switch (field.getFieldType()) {
                case STRING:
                    fieldMapping.put("type", "text");
                    Map<String, Object> keywordField = new HashMap<>();
                    keywordField.put("type", "keyword");
                    fieldMapping.put("fields", Map.of("keyword", keywordField));
                    break;
                case NUMBER:
                    fieldMapping.put("type", "double");
                    break;
                case DATE:
                    fieldMapping.put("type", "date");
                    fieldMapping.put("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis");
                    break;
                case BOOLEAN:
                    fieldMapping.put("type", "boolean");
                    break;
                default:
                    fieldMapping.put("type", "text");
            }
            
            properties.put(field.getFieldName(), fieldMapping);
        }
        
        mapping.put("properties", properties);
        return mapping;
    }

    /**
     * 增强宽表VO对象
     */
    private void enrichWideTableVO(WideTableVO wideTableVO) {
        if (wideTableVO.getType() != null) {
            wideTableVO.setTypeDescription(wideTableVO.getType().getDescription());
        }
        if (wideTableVO.getStatus() != null) {
            wideTableVO.setStatusDescription(wideTableVO.getStatus().getDescription());
        }
    }

    /**
     * 转换字段信息
     */
    private List<WideTableDataVO.FieldInfo> convertToFieldInfos(List<WideTableFieldVO> fields) {
        return fields.stream()
            .map(field -> {
                WideTableDataVO.FieldInfo fieldInfo = new WideTableDataVO.FieldInfo();
                fieldInfo.setFieldName(field.getFieldName());
                fieldInfo.setDisplayName(field.getDisplayName());
                fieldInfo.setFieldType(field.getFieldType().name());
                fieldInfo.setIsDisplayField(field.getIsDisplayField());
                fieldInfo.setIsQueryField(field.getIsQueryField());
                fieldInfo.setSortOrder(field.getSortOrder());
                return fieldInfo;
            })
            .collect(Collectors.toList());
    }
}